package certs

import (
	"context"
	"crypto/rsa"
	"crypto/x509"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/pkg/errors"
	bkev1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	bkeinit "gopkg.openfuyao.cn/bkecommon/cluster/initialize"
	bkenode "gopkg.openfuyao.cn/bkecommon/cluster/node"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"gopkg.openfuyao.cn/bkeagent/pkg/executor/exec"
	"gopkg.openfuyao.cn/bkeagent/pkg/job/builtin/plugin"
	"gopkg.openfuyao.cn/bkeagent/utils"
	"gopkg.openfuyao.cn/bkeagent/utils/log"
	"gopkg.openfuyao.cn/bkeagent/utils/pkiutil"
)

const (
	Name = "Cert"
	// CertChainFileName defines certificate ca and chain name
	CertCAAndChainFileName = "ca-chain.crt"
	// LocalCertDir is the directory where certificates are pushed by ensureBKEAgent
	LocalCertDir = "/etc/openFuyao/certs"
	// LocalTrustChainPath is the local path for trust-chain.crt
	LocalTrustChainPath = LocalCertDir + "/trust-chain.crt"
	// LocalGlobalCACertPath is the local path for global-ca.crt
	LocalGlobalCACertPath = LocalCertDir + "/global-ca.crt"
	// LocalGlobalCAKeyPath is the local path for global-ca.key
	LocalGlobalCAKeyPath = LocalCertDir + "/global-ca.key"
)

type CertPlugin struct {
	k8sClient   client.Client
	bkeConfig   *bkev1beta1.BKEConfig
	exec        exec.Executor
	clusterName string
	namespace   string
	currentNode *bkenode.Node

	altNames []string
	pkiPath  string
}

// kubeConfigServerConfig contains the server configuration for kubeconfig generation
type kubeConfigServerConfig struct {
	isWorker   bool
	serverPort int
	nodeIP     string
	user       string
}

func New(c client.Client, exec exec.Executor, cfg *bkev1beta1.BKEConfig) plugin.Plugin {
	return &CertPlugin{
		k8sClient: c,
		bkeConfig: cfg,
		exec:      exec,
	}
}

func (cp *CertPlugin) Name() string {
	return Name
}
func (cp *CertPlugin) Param() map[string]plugin.PluginParam {
	return map[string]plugin.PluginParam{
		"generate": {
			Key:         "generate",
			Value:       "true,false",
			Required:    false,
			Default:     "true",
			Description: "generate certificates and keys",
		},
		"altDNSNames": {
			Key:         "altDNSNames",
			Value:       "abc.com",
			Required:    false,
			Default:     "",
			Description: "alternative DNS names for cert generation, split by ','",
		},
		"altIPs": {
			Key:         "altIPs",
			Value:       "",
			Required:    false,
			Default:     "127.0.0.1",
			Description: "alternative IPs for cert generation, split by ','",
		},

		"generateKubeConfig": {
			Key:         "kubeConfig",
			Value:       "",
			Required:    false,
			Default:     "false",
			Description: "generate kubeConfig",
		},
		// todo support controller,scheduler
		"localKubeConfigScope": {
			Key:         "localKubeConfigScope",
			Value:       "kubeconfig,controller-manager,scheduler,kubelet,kube-proxy",
			Required:    false,
			Default:     "kubeconfig,controller-manager,scheduler,kubelet,kube-proxy",
			Description: "Specify the scope to generate kubeConfig",
		},

		"loadCACert": {
			Key:         "loadCACert",
			Value:       "true,false",
			Required:    false,
			Default:     "false",
			Description: "load CA cert from cluster api secret",
		},
		"caCertNames": {
			Key:         "caCertNames",
			Value:       "Names1,Names2,...",
			Required:    false,
			Default:     "ca,sa,etcd,proxy",
			Description: "NameSpace/Names of CA cert from cluster api secret,Names optional [ca,sa,etcd,proxy]",
		},

		"loadTargetClusterCert": {
			Key:         "loadTargetClusterCert",
			Value:       "true,false",
			Required:    false,
			Default:     "false",
			Description: "load all cert from cluster api secret, which create by bke",
		},
		"loadAdminKubeconfig": {
			Key:         "loadAdminKubeconfig",
			Value:       "true,false",
			Required:    false,
			Default:     "false",
			Description: "load admin kubeconfig from cluster api secret",
		},

		"uploadCerts": {
			Key:         "upload",
			Value:       "true,false",
			Required:    false,
			Default:     "false",
			Description: "upload certs to manager k8s as secret,",
		},

		"certificatesDir": {
			Key:         "certificatesDir",
			Value:       "",
			Required:    false,
			Default:     pkiutil.GetDefaultPkiPath(),
			Description: "Path to the PKI file,default is /etc/kubernetes/pki ",
		},

		"clusterName": {
			Key:         "clusterName",
			Value:       "",
			Required:    false,
			Default:     "",
			Description: "cluster name",
		},
		"namespace": {
			Key:         "namespace",
			Value:       "",
			Required:    false,
			Default:     "",
			Description: "bke cluster namespace,which is used to load cert secret",
		},
		"isManagerCluster": {
			Key:         "isManagerCluster",
			Value:       "true,false",
			Required:    false,
			Default:     "false",
			Description: "whether the BKECluster is manager",
		},
	}
}

// Execute  implements the plugin.Plugin interface.
// example:
// 1. load CA cert from cluster api secret,and generate other certs with CA cert
// ["Cert", "generate=true","loadCACert=true","caCertNames=ca,sa,kubeconfig,etcd,proxy"]
// 2. generate all certificates required for k8s to run
// ["Cert", "generate=true"]
// 3. load CA cert from cluster api secret, but only loads one or more to generate other certs
// ["Cert", "generate=true","loadCACert=true","caCertNames=ca"]
func (cp *CertPlugin) Execute(commands []string) ([]string, error) {
	certParamMap, err := plugin.ParseCommands(cp, commands)
	if err != nil {
		return nil, err
	}

	if err := cp.initializeParams(certParamMap); err != nil {
		return nil, err
	}

	// loadCACert has highest priority
	if err := cp.handleLoadCACert(certParamMap); err != nil {
		return nil, err
	}

	if err := cp.handleLoadAdminKubeconfig(certParamMap); err != nil {
		return nil, err
	}

	if err := cp.handleCertChainAndGlobalCert(certParamMap); err != nil {
		return nil, err
	}

	if err := cp.handleLoadTargetClusterCert(certParamMap); err != nil {
		return nil, err
	}

	if err := cp.handleGenerateCerts(certParamMap); err != nil {
		return nil, err
	}

	if err := cp.handleGenerateKubeConfig(certParamMap); err != nil {
		return nil, err
	}

	if err := cp.handleUploadCerts(certParamMap); err != nil {
		return nil, err
	}

	return nil, nil
}

// initializeParams initializes plugin parameters and current node from certParamMap
func (cp *CertPlugin) initializeParams(certParamMap map[string]string) error {
	cp.pkiPath = certParamMap["certificatesDir"]
	cp.namespace = certParamMap["namespace"]
	cp.clusterName = certParamMap["clusterName"]

	if cp.bkeConfig != nil {
		nodes := bkenode.Nodes(cp.bkeConfig.Nodes)
		currentNode, err := nodes.CurrentNode()
		if err == nil {
			cp.currentNode = &currentNode
		}
	}
	return nil
}

// handleLoadCACert handles loading CA certificates from cluster API secret
func (cp *CertPlugin) handleLoadCACert(certParamMap map[string]string) error {
	if certParamMap["loadCACert"] != "true" {
		return nil
	}

	if certParamMap["caCertNames"] == "" {
		log.Error("caCertNames is required when loadCACert is true")
		return errors.New("caCertNames is required when loadCACert is true")
	}

	caCertNames := strings.Split(certParamMap["caCertNames"], ",")
	if cp.clusterName == "" || cp.namespace == "" {
		log.Error("clusterName and namespace are required when caCertNames is not empty")
		return errors.New("clusterName and namespace are required when caCertNames is not empty")
	}

	log.Infof("load CA cert from cluster api secret")
	// get CA cert from cluster api secret
	for _, secretName := range caCertNames {
		if err := cp.getCertFromSecret(secretName, cp.pkiPath); err != nil {
			log.Debug(err)
			return errors.Wrapf(err, "failed to get CA cert from namespace %q secret %q", cp.namespace, secretName)
		}
	}

	return nil
}

// handleLoadAdminKubeconfig handles loading admin kubeconfig from cluster API secret
func (cp *CertPlugin) handleLoadAdminKubeconfig(certParamMap map[string]string) error {
	if certParamMap["loadAdminKubeconfig"] != "true" {
		return nil
	}

	log.Infof("load admin kubeconfig from cluster api secret")
	if cp.namespace == "" {
		log.Error("'namespace' is required when 'loadAdminKubeconfig' is true")
		return errors.New("'namespace' is required when 'loadAdminKubeconfig' is true")
	}
	// get kubeconfig from cluster api secret，该kubeconfig是集群的入口
	if err := cp.getCertFromSecret("kubeconfig", pkiutil.KubernetesDir); err != nil {
		log.Debug(err)
		return errors.Wrapf(err, "failed to get kubeConfig from namespace %q secret %q", cp.namespace, "kubeconfig")
	}

	if cp.currentNode != nil {
		cp.copyAdminKubeConfig(cp.currentNode.Username)
	} else {
		cp.copyAdminKubeConfig("root")
	}

	return nil
}

// handleCertChainAndGlobalCert handles management cluster specific logic
func (cp *CertPlugin) handleCertChainAndGlobalCert(certParamMap map[string]string) error {
	if cp.currentNode.IsWorker() {
		return nil
	}
	if certParamMap["isManagerCluster"] != "true" {
		if err := cp.saveCertChainFromLocal(); err != nil {
			log.Warnf("Failed to save cert chain from local: %v", err)
			return nil
		}
		return nil
	}

	log.Infof("detected management cluster, loading global CA from local files")
	if err := cp.loadGlobalCACertFromLocal(); err != nil {
		// 这里不返回错误，如果用户不传入global-ca，走自签证书逻辑，则不需要这一步
		log.Warnf("No global CA from local files: %v", err)
	}

	if err := cp.saveCertChainFromLocal(); err != nil {
		log.Warnf("failed to save cert chain for manager cluster: %v", err)
		// 如果用户不导入证书链，不影响自定义证书签发功能，不返回错误
	}

	return nil
}

// handleLoadTargetClusterCert handles loading all certificates from cluster API secret
func (cp *CertPlugin) handleLoadTargetClusterCert(certParamMap map[string]string) error {
	if certParamMap["loadTargetClusterCert"] != "true" {
		return nil
	}

	log.Infof("load all cert from cluster api secret, which create by bke")
	if cp.namespace == "" {
		return errors.New("'namespace' is required when 'loadTargetClusterCert' is true")
	}
	certList := pkiutil.GetTargetClusterCertList()
	certList.SetPkiPath(cp.pkiPath)
	for _, cert := range certList {
		if err := cp.getCertFromSecret(cert.Name, cp.pkiPath); err != nil {
			return errors.Wrapf(err, "failed to get cert from namespace %q secret %q", cp.namespace, cert.Name)
		}
	}

	return nil
}

// handleGenerateCerts handles certificate generation
func (cp *CertPlugin) handleGenerateCerts(certParamMap map[string]string) error {
	if certParamMap["generate"] != "true" {
		return nil
	}

	log.Infof("generate certificates and keys,if ca.crt and ca.key exist,will generate other certs with ca.crt and ca.key")
	if cp.altNames == nil {
		cp.altNames = []string{}
	}
	if v, ok := certParamMap["altIPs"]; ok {
		altIPs := strings.Split(v, ",")
		cp.altNames = append(cp.altNames, altIPs...)
		log.Infof("add extra altIPs for generate certs: %v", altIPs)
	}
	if v, ok := certParamMap["altDNSNames"]; ok {
		altDNSNames := strings.Split(v, ",")
		cp.altNames = append(cp.altNames, altDNSNames...)
		log.Infof("add extra altDNSNames for generate certs: %v", altDNSNames)
	}

	if err := cp.generateCerts(); err != nil {
		return errors.Wrap(err, "failed to generate certs")
	}

	return nil
}

// handleGenerateKubeConfig handles kubeconfig generation
func (cp *CertPlugin) handleGenerateKubeConfig(certParamMap map[string]string) error {
	if certParamMap["generateKubeConfig"] != "true" {
		return nil
	}

	log.Debug("generate kubeConfig")

	// Determine node type and get server configuration
	config := cp.getKubeConfigServerConfig()
	log.Debugf("apiserver port is %d, nodeIP is %s", config.serverPort, config.nodeIP)

	// Determine scopes to generate
	scopes := strings.Split(certParamMap["localKubeConfigScope"], ",")

	// Generate kubeconfigs for each scope
	if err := cp.generateKubeConfigsForScopes(scopes, config.serverPort, config.nodeIP, config.isWorker); err != nil {
		return err
	}

	// Copy admin kubeconfig to user's home directory (only for master nodes)
	if !config.isWorker && utils.ContainsString(scopes, "kubeconfig") {
		cp.copyAdminKubeConfig(config.user)
	}

	return nil
}

// getKubeConfigServerConfig determines the server configuration based on node type
func (cp *CertPlugin) getKubeConfigServerConfig() kubeConfigServerConfig {
	config := kubeConfigServerConfig{
		serverPort: bkeinit.DefaultAPIBindPort,
		nodeIP:     "127.0.0.1",
		user:       "root",
		isWorker:   false,
	}

	if cp.currentNode == nil {
		return config
	}

	if cp.currentNode.IsWorker() {
		config.isWorker = true
		log.Infof("current node is worker node, will only generate kubelet kubeconfig")
		config.serverPort, config.nodeIP, _ = cp.getNodeServerConfig(true)

	} else {
		config.serverPort, config.nodeIP, config.user = cp.getNodeServerConfig(false)
	}

	return config
}

// IsHACluster checks if the cluster is a HA cluster by checking if there are multiple master nodes
// This matches the logic in generator.go where HA cluster is determined by ControlPlaneEndpoint.Host
// not being in the nodes list. In bkeagent, we use master node count as the indicator.
func (cp *CertPlugin) IsHACluster() bool {
	if cp.bkeConfig == nil {
		return false
	}

	nodes := bkenode.Nodes(cp.bkeConfig.Nodes)

	// If there are multiple master nodes, it's a HA cluster
	return len(nodes.Master()) > 1
}

// getNodeServerConfig gets the server configuration for nodes
// isWorker: true for worker nodes, false for master nodes
// Returns serverPort, nodeIP, and user (user is only meaningful for master nodes)
func (cp *CertPlugin) getNodeServerConfig(isWorker bool) (serverPort int, nodeIP string, user string) {
	serverPort = bkeinit.DefaultAPIBindPort
	nodeIP = "127.0.0.1"
	user = "root"

	if cp.bkeConfig == nil {
		log.Warn("bkeConfig is nil, using default serverPort and nodeIP")
		return serverPort, nodeIP, user
	} else if cp.bkeConfig.Cluster.APIServer != nil && cp.bkeConfig.Cluster.APIServer.Port != 0 {
		serverPort = int(cp.bkeConfig.Cluster.APIServer.Port)
	}

	if isWorker {

		// Use IsHACluster to determine if this is a HA cluster
		if cp.IsHACluster() {
			// HA cluster: use HA domain (master.bocloud.com) and load balancer port
			// This matches the logic in generator.go where HA cluster kubeconfig uses the load balancer domain
			nodeIP = pkiutil.MasterHADomain
			serverPort = bkeinit.DefaultLoadBalancerBindPort
			log.Infof("HA cluster detected: using HA domain %s with port %d", nodeIP, serverPort)
			return serverPort, nodeIP, user
		}
		nodes := bkenode.Nodes(cp.bkeConfig.Nodes)
		masterNodes := nodes.Master()
		// Single master cluster: use the master node IP
		nodeIP = masterNodes[0].IP
		log.Infof("single master cluster: using master node IP %s with port %d", nodeIP, serverPort)
		return serverPort, nodeIP, user
	}

	// Master node configuration
	// Check if currentNode is not nil before accessing
	if cp.currentNode != nil {
		if cp.currentNode.APIServer != nil && cp.currentNode.APIServer.Port != 0 {
			serverPort = int(cp.currentNode.APIServer.Port)
		}
		if cp.currentNode.IP != "" {
			nodeIP = cp.currentNode.IP
		}
		if cp.currentNode.Username != "" {
			user = cp.currentNode.Username
		}
	}

	return serverPort, nodeIP, user
}

// generateKubeConfigsForScopes generates kubeconfigs for the given scopes
func (cp *CertPlugin) generateKubeConfigsForScopes(scopes []string, serverPort int, nodeIP string, isWorker bool) error {
	for _, scope := range scopes {
		// 该方法创建的kubeconfig文件，只能用于当前节点，固定存储在/etc/kubernetes目录下
		// For worker node's kubelet kubeconfig, nodeIP is already set correctly:
		// - HA cluster: nodeIP is MasterHADomain (master.bocloud.com)
		// - Single master: nodeIP is the master node IP
		kubeConfigGenerater := pkiutil.NewKubeConfigGenerater(pkiutil.KubeConfigOptions{
			PkiPath:     cp.pkiPath,
			ClusterName: cp.clusterName,
			FileName:    scope,
			ServerPort:  strconv.Itoa(serverPort),
			HostIP:      nodeIP,
			BKEConfig:   cp.bkeConfig,
		})
		if err := kubeConfigGenerater.Generate(); err != nil {
			return errors.Errorf("failed to generate local kubeconfig for %s, err: %v", scope, err)
		}
	}
	return nil
}

// handleUploadCerts handles uploading certificates to manager k8s as secret
func (cp *CertPlugin) handleUploadCerts(certParamMap map[string]string) error {
	if certParamMap["uploadCerts"] != "true" {
		return nil
	}

	log.Infof("upload all certs to manager k8s as secret")
	if cp.namespace == "" {
		log.Error("'namespace' is required when 'uploadCerts' is true")
		return errors.New("'namespace' is required when 'uploadCerts' is true")
	}
	if err := cp.uploadCerts(cp.namespace); err != nil {
		return errors.Wrap(err, "failed to upload certs")
	}

	return nil
}

// generateCerts generates all the certificates and keys necessary to run k8s
// If the CA certificate exists, use the existing CA certificate to generate other certificates
func (cp *CertPlugin) generateCerts() error {
	certList, err := cp.prepareCertList()
	if err != nil {
		return err
	}
	certList.SetPkiPath(cp.pkiPath)
	var lastCACert *pkiutil.BKECert
	for _, cert := range certList {
		if cert.CAName == "" {
			if err := pkiutil.GenerateCACert(cert); err != nil {
				return err
			}
			lastCACert = cert
		} else {
			if err := pkiutil.GenerateCertWithCA(cert, lastCACert); err != nil {
				return err
			}
		}
	}

	if err := pkiutil.GenerateRSACert(pkiutil.BKECertServiceAccount()); err != nil {
		return err
	}

	return nil
}

// uploadCerts uploads all the certificates and keys to the manager k8s, expect the CA cert
func (cp *CertPlugin) uploadCerts(nameSpace string) error {
	errInfo := "failed to upload cert"
	certList := pkiutil.GetCertsWithoutCA()
	certList.SetPkiPath(cp.pkiPath)
	for _, cert := range certList {
		if err := pkiutil.CertExists(cert); err != nil {
			return errors.Wrapf(err, "%s %q", errInfo, cert.Name)
		}
	}
	for _, cert := range certList {
		if err := cp.uploadCertToSecret(cert, nameSpace); err != nil {
			return errors.Wrapf(err, "%s %q", errInfo, cert.Name)
		}
	}
	return nil
}

func (cp *CertPlugin) getCertFromSecret(name, saveTo string) error {
	certSecret := &corev1.Secret{}

	certName := fmt.Sprintf("%s-%s", cp.clusterName, name)

	log.Debugf("get cert %q from secret %s", name, cp.namespace+"/"+certName)

	err := cp.k8sClient.Get(context.Background(), client.ObjectKey{Namespace: cp.namespace, Name: certName}, certSecret)
	if err != nil {
		return err
	}

	if err := pkiutil.StoreClusterAPICert(certSecret, saveTo); err != nil {
		return err
	}
	return nil
}

func (cp *CertPlugin) uploadCertToSecret(certSpec *pkiutil.BKECert, namespace string) error {
	return pkiutil.UploadBKECertToClusterAPI(cp.k8sClient, certSpec, namespace, cp.clusterName)
}

// prepareCertList returns a pkiutil.Certificates object
// fill each certificate the AltNames fields from the kubeadm config and node.json
func (cp *CertPlugin) prepareCertList() (pkiutil.Certificates, error) {
	certList := pkiutil.GetDefaultCertList()

	if cp.bkeConfig == nil {
		return certList, nil
	}

	for _, cert := range certList {
		switch cert.BaseName {
		case pkiutil.APIServerCertAndKeyBaseName:
			altNames, err := pkiutil.GetAPIServerCertAltNamesFromBkeConfig(cp.bkeConfig)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to get alt names from bke config")
			}
			cert.Config.AltNames.DNSNames = append(cert.Config.AltNames.DNSNames, altNames.DNSNames...)
			cert.Config.AltNames.IPs = append(cert.Config.AltNames.IPs, altNames.IPs...)
			if err := pkiutil.AppendSANsToAltNames(&cert.Config.AltNames, cp.altNames, cert.BaseName); err != nil {
				return nil, errors.Wrapf(err, "failed to append alt names to %q", cert.BaseName)
			}
		case pkiutil.EtcdServerCertAndKeyBaseName:
			altnames, err := pkiutil.GetEtcdCertAltNamesFromBkeConfig(cp.bkeConfig, true)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to get alt names from bke config")
			}
			cert.Config.AltNames.DNSNames = append(cert.Config.AltNames.DNSNames, altnames.DNSNames...)
			cert.Config.AltNames.IPs = append(cert.Config.AltNames.IPs, altnames.IPs...)
			if err := pkiutil.AppendSANsToAltNames(&cert.Config.AltNames, cp.altNames, cert.BaseName); err != nil {
				return nil, errors.Wrapf(err, "failed to append alt names to %q", cert.BaseName)
			}
		case pkiutil.EtcdPeerCertAndKeyBaseName:
			altnames, err := pkiutil.GetEtcdCertAltNamesFromBkeConfig(cp.bkeConfig, true)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to get alt names from bke config")
			}
			cert.Config.AltNames.DNSNames = append(cert.Config.AltNames.DNSNames, altnames.DNSNames...)
			cert.Config.AltNames.IPs = append(cert.Config.AltNames.IPs, altnames.IPs...)
			if err := pkiutil.AppendSANsToAltNames(&cert.Config.AltNames, cp.altNames, cert.BaseName); err != nil {
				return nil, errors.Wrapf(err, "failed to append alt names to %q", cert.BaseName)
			}
		}
	}

	return certList, nil
}

// splitNameSpaceName split name space and name
func splitNameSpaceName(nn string) (string, []string, error) {
	ns := strings.Split(nn, ":")
	if len(ns) != 2 {
		return "", nil, errors.New("invalid namespace:name format")
	}
	return ns[0], strings.Split(ns[1], ","), nil
}

// copyAdminKubeConfig copy admin kube config to user's home dir and root's home dir
func (cp *CertPlugin) copyAdminKubeConfig(user string) {
	// ignore error
	rootDir := "/root/.kube"
	configDir := "/root/.kube"

	if user != "root" {
		configDir = fmt.Sprintf("/home/%s/.kube", user)
	}

	cmd := fmt.Sprintf("cp -f /etc/kubernetes/admin.conf %s/config", configDir)

	if configDir == rootDir && !utils.Exists(configDir) {
		err := os.MkdirAll(configDir, utils.RwxRxRx)
		if err != nil {
			log.Warnf("(ignore) failed to mkdir %s, err: %v", rootDir, err)
		}
	} else {
		err := os.MkdirAll(configDir, utils.RwxRxRx)
		if err != nil {
			log.Warnf("(ignore) failed to mkdir %s, err: %v", configDir, err)
		}
		err = os.MkdirAll(rootDir, utils.RwxRxRx)
		if err != nil {
			log.Warnf("(ignore) failed to mkdir %s, err: %v", rootDir, err)
		}
		cmd = cmd + fmt.Sprintf(" && cp -f /etc/kubernetes/admin.conf %s/config", rootDir)
		cmd = cmd + fmt.Sprintf(" && chown -R %s:%s %s", user, user, configDir)
	}

	// todo 合并到一个文件中并设置kubectl context
	out, err := cp.exec.ExecuteCommandWithOutput("/bin/sh", "-c", cmd)
	if err != nil {
		log.Warnf("(ignore) failed to cp /etc/kubernetes/admin.conf, output: %s, error: %v", out, err)
	}
}

// loadGlobalCACertFromLocal loads the global CA from local files and saves it to the local filesystem
// It also saves the cert chain (ca-chain.crt) and global-ca.crt/global-ca.key
func (cp *CertPlugin) loadGlobalCACertFromLocal() error {
	// 从本地读取 global-ca.crt 和 global-ca.key
	certBytes, err := os.ReadFile(LocalGlobalCACertPath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Infof("global-ca.crt not found at %s, skipping global CA loading", LocalGlobalCACertPath)
			return nil
		}
		return errors.Wrapf(err, "failed to read global-ca.crt from %s", LocalGlobalCACertPath)
	}

	keyBytes, err := os.ReadFile(LocalGlobalCAKeyPath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Infof("global-ca.key not found at %s, skipping global CA loading", LocalGlobalCAKeyPath)
			return nil
		}
		return errors.Wrapf(err, "failed to read global-ca.key from %s", LocalGlobalCAKeyPath)
	}

	if len(certBytes) == 0 || len(keyBytes) == 0 {
		log.Infof("global-ca.crt or global-ca.key is empty")
		return nil
	}

	cert, key, err := cp.parseGlobalCACertAndKey(certBytes, keyBytes)
	if err != nil {
		return err
	}

	// 保存 global-ca.crt 和 global-ca.key 到 pki 目录
	if err := cp.saveGlobalCACertAndKey(cert, key); err != nil {
		return err
	}

	return nil
}

// getGlobalCASecretFromParentCluster gets the global CA Secret from the parent cluster
func (cp *CertPlugin) getGlobalCASecretFromParentCluster() (*corev1.Secret, error) {
	globalCASecret := &corev1.Secret{}
	secretNamespace := utils.GlobalCANamespace
	secretName := utils.GlobalCASecretName

	log.Debugf("loading global CA from parent cluster secret %s/%s", secretNamespace, secretName)

	err := cp.k8sClient.Get(
		context.Background(),
		client.ObjectKey{Namespace: secretNamespace, Name: secretName},
		globalCASecret,
	)
	if err != nil {
		if apierrors.IsNotFound(err) {
			log.Infof("global CA secret %s/%s not in parent cluster", secretNamespace, secretName)
			return nil, nil
		}
		return nil, errors.Wrapf(err, "failed to get global CA secret %s/%s from parent cluster", secretNamespace, secretName)
	}
	return globalCASecret, nil
}

// validateGlobalCASecretData validates whether the global CA Secret data is valid
func (cp *CertPlugin) validateGlobalCASecretData(secret *corev1.Secret) ([]byte, []byte, error) {
	secretNamespace := utils.GlobalCANamespace
	secretName := utils.GlobalCASecretName

	if secret.Data == nil {
		log.Warnf("global CA secret %s/%s has no data", secretNamespace, secretName)
		return nil, nil, nil
	}

	certBytes, hasCert := secret.Data[pkiutil.TLSCrtDataName]
	keyBytes, hasKey := secret.Data[pkiutil.TLSKeyDataName]

	if !hasCert || !hasKey {
		log.Warnf("global CA secret %s/%s missing certificate or key data", secretNamespace, secretName)
		return nil, nil, nil
	}

	if len(certBytes) == 0 || len(keyBytes) == 0 {
		log.Warnf("global CA secret %s/%s has empty data", secretNamespace, secretName)
		return nil, nil, nil
	}

	return certBytes, keyBytes, nil
}

// parseGlobalCACertAndKey parses the global CA certificate and key
func (cp *CertPlugin) parseGlobalCACertAndKey(certBytes, keyBytes []byte) (*x509.Certificate, *rsa.PrivateKey, error) {
	certs, err := pkiutil.ParseCertsPEM(certBytes)
	if err != nil {
		return nil, nil, errors.Wrap(err, "failed to parse global CA certificate")
	}

	key, err := pkiutil.ParsePrivateKeyPEM(keyBytes)
	if err != nil {
		return nil, nil, errors.Wrap(err, "failed to parse global CA key")
	}

	return certs[0], key, nil
}

// saveGlobalCACertAndKey saves the global CA certificate and key to the local filesystem
func (cp *CertPlugin) saveGlobalCACertAndKey(cert *x509.Certificate, key *rsa.PrivateKey) error {
	globalCACert := &pkiutil.BKECert{
		Name:     "global-ca",
		BaseName: pkiutil.GlobalCACertAndKeyBaseName,
		IsCA:     true,
		Config:   pkiutil.CertConfig{},
		PkiPath:  cp.pkiPath,
	}

	if err := pkiutil.WriteCertAndKey(globalCACert, cert, key); err != nil {
		return errors.Wrap(err, "failed to write global CA certificate and key")
	}
	return nil
}

// saveCertChain saves the certificate chain to local filesystem. If the chain exists in the Secret,
// it gets the CA cert from cluster CA secret and merges them together
func (cp *CertPlugin) saveCertChain(secret *corev1.Secret) {
	if secret.Data == nil {
		log.Infof("global CA secret %s/%s has no data for cert chain", secret.Namespace, secret.Name)
		return
	}

	chainBytes, hasChain := secret.Data[pkiutil.ChainCrtDataName]
	if !hasChain || len(chainBytes) == 0 {
		log.Infof("global CA secret %s/%s missing chain data", secret.Namespace, secret.Name)
		return
	}

	caCertBytes, err := cp.getCACertFromClusterSecret()
	if err != nil {
		log.Warnf("failed to get CA cert from cluster secret: %v", err)
		return
	}

	chainCerts, err := cp.parseChainCerts(chainBytes)
	if err != nil {
		log.Warnf("failed to parse certificate chain: %v", err)
		return
	}

	allCerts, err := cp.mergeCertChain(caCertBytes, chainBytes)
	if err != nil {
		log.Warnf("failed to merge certificate chain: %v", err)
		return
	}

	chainPath := filepath.Join(cp.pkiPath, pkiutil.CertChainFileName)
	caChainPath := filepath.Join(cp.pkiPath, CertCAAndChainFileName)

	if err := cp.writeCertChainToFile(chainPath, chainCerts); err != nil {
		log.Warnf("failed to write chain only file: %v", err)
	}

	if err := cp.writeCertChainToFile(caChainPath, allCerts); err != nil {
		log.Warnf("failed to write CA and chain file: %v", err)
		return
	}
}

// saveCertChainFromLocal saves the certificate chain from local filesystem
// It reads trust-chain.crt from local file and gets CA cert from cluster secret, then merges them
// If trust-chain.crt doesn't exist, it returns without saving anything (ca.crt is only for serving chain.crt)
// If CA cert cannot be obtained, it will only save chain (if available)
func (cp *CertPlugin) saveCertChainFromLocal() error {
	// 从本地读取 trust-chain.crt（如果存在）
	chainBytes, err := os.ReadFile(LocalTrustChainPath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Infof("trust-chain.crt not found at %s, skipping cert chain generation (ca.crt is only for serving chain.crt)", LocalTrustChainPath)
			// 如果没有 trust-chain，不需要保存 CA cert，因为 ca.crt 只是为了服务 chain.crt
			return nil
		}
		return errors.Wrapf(err, "failed to read trust-chain.crt from %s", LocalTrustChainPath)
	}

	if len(chainBytes) == 0 {
		log.Infof("trust-chain.crt at %s is empty, skipping cert chain generation (ca.crt is only for serving chain.crt)", LocalTrustChainPath)
		// 如果 trust-chain 为空，不需要保存 CA cert
		return nil
	}

	// 从 secret 获取 ca.crt
	caCertBytes, err := cp.getCACertFromClusterSecret()
	if err != nil {
		log.Warnf("failed to get CA cert from cluster secret: %v, will only save chain", err)
		// 如果无法获取 CA cert，只保存 chain
		chainCerts, err := cp.parseChainCerts(chainBytes)
		if err != nil {
			return errors.Wrap(err, "failed to parse certificate chain")
		}
		chainPath := filepath.Join(cp.pkiPath, pkiutil.CertChainFileName)
		return cp.writeCertChainToFile(chainPath, chainCerts)
	}

	chainCerts, err := cp.parseChainCerts(chainBytes)
	if err != nil {
		return errors.Wrap(err, "failed to parse certificate chain")
	}

	allCerts, err := cp.mergeCertChain(caCertBytes, chainBytes)
	if err != nil {
		return errors.Wrap(err, "failed to merge certificate chain")
	}

	chainPath := filepath.Join(cp.pkiPath, pkiutil.CertChainFileName)
	caChainPath := filepath.Join(cp.pkiPath, CertCAAndChainFileName)

	if err := cp.writeCertChainToFile(chainPath, chainCerts); err != nil {
		// 证书链写入失败不影响后续ca-chain.crt写入并挂载apiserver
		log.Warnf("failed to write chain only file: %v", err)
	}

	if err := cp.writeCertChainToFile(caChainPath, allCerts); err != nil {
		return errors.Wrap(err, "failed to write CA and chain file")
	}

	log.Infof("successfully saved cert chain from local files")
	return nil
}

// getCACertFromClusterSecret gets the CA certificate from the cluster CA secret
func (cp *CertPlugin) getCACertFromClusterSecret() ([]byte, error) {
	if cp.clusterName == "" || cp.namespace == "" {
		return nil, errors.New("clusterName and namespace are required to get CA cert")
	}

	caSecretName := fmt.Sprintf("%s-%s", cp.clusterName, pkiutil.CACertAndKeyBaseName)
	caSecret := &corev1.Secret{}

	log.Debugf("get CA cert from secret %s/%s", cp.namespace, caSecretName)

	err := cp.k8sClient.Get(context.Background(), client.ObjectKey{Namespace: cp.namespace, Name: caSecretName}, caSecret)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to get CA secret %s/%s", cp.namespace, caSecretName)
	}

	if caSecret.Data == nil {
		return nil, errors.Errorf("CA secret %s/%s has no data", cp.namespace, caSecretName)
	}

	caCertBytes, hasCert := caSecret.Data[pkiutil.TLSCrtDataName]
	if !hasCert || len(caCertBytes) == 0 {
		return nil, errors.Errorf("CA secret %s/%s missing tls.crt data", cp.namespace, caSecretName)
	}

	return caCertBytes, nil
}

// parseChainCerts parses the certificate chain byte array into a certificate list
func (cp *CertPlugin) parseChainCerts(chainBytes []byte) ([]*x509.Certificate, error) {
	chainCerts, err := pkiutil.ParseCertsPEM(chainBytes)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse certificate chain")
	}
	return chainCerts, nil
}

// mergeCertChain merges the CA certificate and certificate chain
func (cp *CertPlugin) mergeCertChain(caCertBytes, chainBytes []byte) ([]*x509.Certificate, error) {
	caCerts, err := pkiutil.ParseCertsPEM(caCertBytes)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse CA certificate")
	}

	chainCerts, err := pkiutil.ParseCertsPEM(chainBytes)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse certificate chain")
	}

	allCerts := append(caCerts, chainCerts...)
	return allCerts, nil
}

// writeCertChainToFile writes the certificate chain to the specified file path
func (cp *CertPlugin) writeCertChainToFile(filePath string, certs []*x509.Certificate) error {
	if !utils.Exists(cp.pkiPath) {
		if err := os.MkdirAll(cp.pkiPath, utils.RwxRxRx); err != nil {
			return errors.Wrapf(err, "failed to create directory %s", cp.pkiPath)
		}
	}

	chainPEM := cp.encodeCertsToPEM(certs)
	if err := os.WriteFile(filePath, chainPEM, utils.RwRR); err != nil {
		return errors.Wrapf(err, "failed to write certificate chain to %s", filePath)
	}
	log.Infof("saved certificate chain to %s", filePath)
	return nil
}

// encodeCertsToPEM encodes the certificate list to PEM format
func (cp *CertPlugin) encodeCertsToPEM(certs []*x509.Certificate) []byte {
	if len(certs) == 0 {
		return nil
	}
	chainPEM := pkiutil.EncodeCertToPEM(certs[0])
	for i := 1; i < len(certs); i++ {
		chainPEM = append(chainPEM, pkiutil.EncodeCertToPEM(certs[i])...)
	}
	return chainPEM
}

// loadConfigMapFromParentCluster load config from cluster api and save locally
func (cp *CertPlugin) loadConfigMapFromParentCluster() error {
	log.Infof("loading cert config ConfigMap %s/%s from parent cluster", pkiutil.CertConfigMapNamespace, pkiutil.CertConfigMapName)
	cm := &corev1.ConfigMap{}
	err := cp.k8sClient.Get(
		context.Background(),
		client.ObjectKey{Namespace: pkiutil.CertConfigMapNamespace, Name: pkiutil.CertConfigMapName},
		cm,
	)
	if err != nil {
		if apierrors.IsNotFound(err) {
			log.Infof("ConfigMap %s/%s not found in parent cluster", pkiutil.CertConfigMapNamespace, pkiutil.CertConfigMapName)
			return nil
		}
		log.Infof("get ConfigMap %s/%s from parent cluster", pkiutil.CertConfigMapNamespace, pkiutil.CertConfigMapName)
		return err
	}
	if len(cm.Data) == 0 {
		log.Infof("ConfigMap %s/%s has no data, nothing to write", pkiutil.CertConfigMapNamespace, pkiutil.CertConfigMapName)
		return nil
	}
	if err := os.MkdirAll(pkiutil.CertConfigDir, utils.RwxRxRx); err != nil {
		return errors.Wrapf(err, "mkdir %s", pkiutil.CertConfigDir)
	}
	written := 0
	for name, content := range cm.Data {
		if !strings.HasSuffix(name, ".json") {
			log.Debugf("skip non-json entry %s in ConfigMap", name)
			continue
		}
		path := filepath.Join(pkiutil.CertConfigDir, name)
		if err := os.WriteFile(path, []byte(content), utils.RwRR); err != nil {
			log.Warnf("write file %s failed: %v", path, err)
			continue
		}
		written++
	}
	if written == 0 {
		log.Infof("no json entries written from ConfigMap %s/%s", pkiutil.CertConfigMapNamespace, pkiutil.CertConfigMapName)
	}
	return nil
}

// loadKubeConfigFromClusterAPI load user custom csr and signing policy config from cluster api
func (cp *CertPlugin) loadKubeConfigFromClusterAPI() error {
	certList := pkiutil.GetKubeConfigs()
	for i, cert := range certList {
		if cert.Name == "kubeconfig" {
			certList[i].Name = "admin"
		}
		if err := cp.getCertFromSecret(cert.Name, cert.PkiPath); err != nil {
			log.Errorf("failed to get cert from namespace %q secret %q", cp.namespace, cert.Name)
			return err
		}
	}
	return nil
}
